################################################################################
## NAME: helix 
## DATE: 2011-05-10
## AUTHOR: Matt Reid
## ID: $Id: helix.py 3 2011-05-20 22:18:11Z themattreid $
## WEBSITE: http://kontrollsoft.com
## EMAIL: mreid@kontrollsoft.com
## LICENSE: BSD http://www.opensource.org/licenses/bsd-license.php
## PURPOSE: Provides classes for commonly used tasks
################################################################################
import sys
import re
import urllib2
import commands
import datetime
import logging
import MySQLdb
import getopt
import time
import string
import random
import socket
import mimetypes

################################################################################
## CLASSES
################################################################################

class dbops:
    '''
    @PURPOSE: Class to provide standard operations for MySQL database interaction
    @RETURNS: per function as follows
    @connect: returns a usable database connection
    @execute: returns result of the query execution
    @row: returns single row output of query as a list
    @rows: returns multiple row output of query as a list
    @USAGE:    
    import helix
    db = helix.dbops()
    conn = db.connect("localhost","root","password","mysql")    
    print db.rows(conn,"show tables")
    '''

    __shared_state = {}
    def __init__(self):
        self.__dict__ = self.__shared_state

    def connect(self,host,user,passwd,db):
        self.host = host
        self.user = user
        self.password = passwd
        self.db = db
        
        try:
            db = MySQLdb.connect(
                host = host,
                user = user,
                passwd = passwd,
                port = 3306,
                db = db)
        except MySQLdb.Error, e:
            print "Error %d: %s" % (e.args[0], e.args[1])
            return False

        return db

    def execute(self,db,query):
        self.db = db
        self.query = query
        cursor = db.cursor()
        try:
            res = cursor.execute(query)
        except MySQLdb.Error, e:
            print "Error %d: %s" % (e.args[0], e.args[1])
            return False
        cursor.close()
        db.commit()
        if res == False:
            res = True
        return res
    
    def row(self,db,query):
        self.db = db
        self.query = query
        c = db.cursor(MySQLdb.cursors.DictCursor)        
        try:
            c.execute(query)
        except MySQLdb.Error, e:
            print "Error %d: %s" % (e.args[0], e.args[1])
            return False
        row = c.fetchone()
        c.close()
        return row
    
    def rows(self,db,query):
        self.db = db
        self.query = query
        c = db.cursor(MySQLdb.cursors.DictCursor)
        try:
            c.execute(query)
        except MySQLdb.Error, e:
            print "Error %d: %s" % (e.args[0], e.args[1])
            return False
        rows = c.fetchall()
        c.close()
        return rows


class urlDelay(urllib2.BaseHandler):
    '''
    @PURPOSE: Prevents overloading the remote web server by delaying requests in random values up to the delay value
    @RETURNS: a modified urllib2 connection header with the added connection delay time
    @USAGE:
    import helix
    delay = helix.urlDelay()
    conn = urllib2.build_opener(delay)
    page = conn.open(link)
    '''    
    
    __shared_state = {}
    def __init__(self,delay):
        self.__dict__ = self.__shared_state
        self.throttleDelay = delay
        if not hasattr(self,'lastRequestTime'):
            self.lastRequestTime = {}
        
    def default_open(self,request):
        currentTime = time.time()
        if ((request.host in self.lastRequestTime) and (time.time() - self.lastRequestTime[request.host] < self.throttleDelay)):
            self.throttleTime = (self.throttleDelay - (currentTime - self.lastRequestTime[request.host]))
            time.sleep(self.throttleTime)
        self.lastRequestTime[request.host] = currentTime
        return None

    def http_response(self,request,response):
        if hasattr(self,'throttleTime'):
            response.info().addheader("x-throttling", "%s seconds"%(self.throttleTime))
            del(self.throttleTime)
        return response


class extensionCheck:
    '''
    @PURPOSE: do a basic check of the link filename extension. This will return "None" in
    the event that we cannot determine the file type
    @RETURNS: string with mimetype extension best-guess
    @USAGE:
    import helix
    ext = helix.extensionCheck()
    file_extension = ext.guess(filename)
    '''

    __shared_state = {}
    def __init__(self):
        self.__dict__ = self.__shared_state
        
    def guess(self,link):
        return str(mimetypes.guess_type(link)[0])


################################################################################
## FUNCTIONS
################################################################################
def sysexec(command):
    '''
    @PURPOSE: provide a simple function to allow arbitrary commands to be executed. 
    @RETURNS: list [execution timing, execution code, execution output]
    @USAGE: 
    import helix
    a = helix.sysexec("ls -al")
    print "timing: %s"%(str(a[0]))
    print "retcode: %s"%(str(a[1]))
    print "output: %s"%(str(a[2]))
    '''
    start = datetime.datetime.now()
    retcode, output = commands.getstatusoutput(command)
    end = datetime.datetime.now()
    timing = end - start
    return [timing,retcode,output]


def versionIdent():
    '''
    @PURPOSE: provides the version of Python for the current environment
    @RETURNS: version of python in major.minor format
    @USAGE:
    import helix
    version = helix.versionIdent()
    '''
    
    ver = sys.version.split(' ')[0].split(".")
    major=ver[:1]
    minor=ver[1:2]
    return [major[0],minor[0]]


def randstr(length):
    '''
    @PURPOSE: generate a random string of requested length
    @RETURNS: a random string of requested length
    @USAGE:
    import helix
    random_string = helix.randstr(32)
    '''
    
    twoletters = [c+d for c in string.letters for d in string.letters]
    r = random.random
    n = len(twoletters)
    l2 = length//2
    lst = [None] * l2
    for i in xrange(l2):
        lst[i] = twoletters[int(r() * n)]
    if length & 1:
        lst.append(random.choice(string.letters))
    return "".join(lst)


def human(bytes):
    '''
    @PURPOSE: format incoming byte value as human readable values
    @RETURNS: string with formatted values
    @USAGE:
    import helix
    value = helix.human(bytes)
    '''
    
    bytes = float(bytes)
    if bytes >= 1099511627776:
        terabytes = bytes / 1099511627776
        size = '%.0fT' % terabytes
    elif bytes >= 1073741824:
        gigabytes = bytes / 1073741824
        size = '%.0fG' % gigabytes
    elif bytes >= 1048576:
        megabytes = bytes / 1048576
        size = '%.0fM' % megabytes
    elif bytes >= 1024:
        kilobytes = bytes / 1024
        size = '%.0fK' % kilobytes
    else:
        size = '%.0fb' % bytes
    return size
    
